package com.bandi.tharshesnows.service;

import java.util.List;

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Build;

import com.bandi.tharshesnows.GetDataCallback;
import com.bandi.tharshesnows.model.County;
import com.bandi.tharshesnows.model.Resort;
import com.bandi.tharshesnows.model.Skirun;
import com.bandi.tharshesnows.persistence.DAOException;
import com.bandi.tharshesnows.persistence.PersistenceClient;

@SuppressLint("NewApi")
public class SkirunsManager {

  private PersistenceClient persistenceClient;

  public SkirunsManager() {
    persistenceClient = PersistenceClient.getInstance();
  }

  public void getCounty(final long countyId, GetDataCallback<County> callback) {
    GetDataTask<County> getCountyTask = new GetDataTask<County>(callback) {

      @Override
      protected County task() throws Throwable {
        County c = persistenceClient.getCountyDAO().get(countyId);
        c.setResorts(persistenceClient.getLocalityDAO().getForCounty(c));
        for (Resort l : c.getResorts()) {
          l.setSkiruns(persistenceClient.getSkirunDAO().getForLocality(l));
        }
        return c;
      }
    };
    getCountyTask.run();
  }

  public void getAllCounties(GetDataCallback<List<County>> callback) {
    GetDataTask<List<County>> getAllCountiesTask = new GetDataTask<List<County>>(callback) {

      @Override
      protected List<County> task() throws Throwable {
        List<County> counties = persistenceClient.getCountyDAO().getAll();
        for (County county : counties) {
          county.setResorts(persistenceClient.getLocalityDAO().getForCounty(county));
          for (Resort locality : county.getResorts()) {
            locality.setSkiruns(persistenceClient.getSkirunDAO().getForLocality(locality));
          }
        }
        return counties;
      }
    };
    getAllCountiesTask.run();
  }

  private Resort saveLocality(Resort locality, County county) throws DAOException {
    if (locality.getCounty().get_id() == -1) {
      locality.getCounty().set_id(county.get_id());
    }
    Resort persistedLocality = persistenceClient.getLocalityDAO().get(locality.getName(), county);
    if (persistedLocality == null) {
      // create
      locality = persistenceClient.getLocalityDAO().create(locality);
      for (Skirun skirun : locality.getSkiruns()) {
        saveSkirun(skirun, locality);
      }
    }
    else {
      // update
      locality.set_id(persistedLocality.get_id());
      locality = persistenceClient.getLocalityDAO().update(locality);
      for (Skirun skirun : locality.getSkiruns()) {
        saveSkirun(skirun, locality);
      }
    }
    return locality;
  }

  private Skirun saveSkirun(Skirun skirun, Resort locality) throws DAOException {
    if (skirun.getResort().get_id() == -1) {
      skirun.getResort().set_id(locality.get_id());
    }
    Skirun persistedSkirun = persistenceClient.getSkirunDAO().get(skirun.getName(), locality);
    if (persistedSkirun == null) {
      // create
      skirun = persistenceClient.getSkirunDAO().create(skirun);
    }
    else {
      // update
      persistedSkirun.setSkirunStatus(skirun.getSkirunStatus());
      if (persistedSkirun.isDownloaded()) {
        copySkirun(persistedSkirun, skirun);
      }
      skirun = persistenceClient.getSkirunDAO().update(persistedSkirun);
    }
    return skirun;
  }

  private void copySkirun(Skirun source, Skirun dest) {
    dest.set_id(source.get_id());
    dest.setCanons(source.hasCanons());
    dest.setDestinationAltitude(source.getDestinationAltitude());
    dest.setDifficulty(source.getDifficulty());
    dest.setDownloaded(source.isDownloaded());
    dest.setInclination(source.getInclination());
    dest.setLength(source.getLength());
    dest.setLocation(source.getLocation());
    dest.setMeteoURL(source.getMeteoURL());
    dest.setName(source.getName());
    dest.setNightmode(source.isNightmode());
    dest.setPictures(source.getPictures());
    dest.setRemoteURL(source.getRemoteURL());
    dest.setSchedule(source.getSchedule());
    dest.setSkirunStatus(source.getSkirunStatus());
    dest.setSkiTransports(source.getSkiTransports());
    dest.setSnowHeight(source.getSnowHeight());
    dest.setStarred(source.isStarred());
    dest.setStartingAltitude(source.getStartingAltitude());
    dest.setUpdated(source.getUpdated());
    dest.setWidth(source.getWidth());
    dest.setObservations(source.getObservations());
  }

  public void updateSkirun(final Skirun skirun, GetDataCallback<Skirun> callback) {
    GetDataTask<Skirun> task = new GetDataTask<Skirun>(callback) {

      @Override
      protected Skirun task() throws Throwable {
        return persistenceClient.getSkirunDAO().update(skirun);
      }
    };
    task.run();
  }

  public void saveCounties(final List<County> counties, GetDataCallback<List<County>> callback) {
    GetDataTask<List<County>> saveCountiesTask = new GetDataTask<List<County>>(callback) {

      @Override
      protected List<County> task() throws Throwable {
        for (County county : counties) {
          County persistedCounty = persistenceClient.getCountyDAO().get(county.getName());
          if (persistedCounty == null) {
            // create new
            county = persistenceClient.getCountyDAO().create(county);
            for (Resort locality : county.getResorts()) {
              saveLocality(locality, county);
            }
          }
          else {
            // update
            county.set_id(persistedCounty.get_id());
            for (Resort locality : county.getResorts()) {
              saveLocality(locality, county);
            }
          }
        }
        return counties;
      }
    };
    saveCountiesTask.run();
  }

  private abstract class GetDataTask<T> extends AsyncTask<Void, Void, Object> {

    private GetDataCallback<T> callback;

    public GetDataTask(GetDataCallback<T> callback) {
      super();
      this.callback = callback;
    }

    @Override
    protected final Object doInBackground(Void... params) {
      try {
        return task();
      }
      catch (Throwable t) {
        return t;
      }
    }

    @Override
    protected final void onPostExecute(Object result) {
      if (result instanceof Throwable) {
        callback.onError((Throwable) result);
      }
      else {
        callback.onData((T) result);
      }
    }

    protected abstract T task() throws Throwable;

    public final void run() {
      if (Build.VERSION.SDK_INT >= 11) {
        executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
      }
      else {
        execute();
      }
    }
  }

}
